﻿#include "Mat44.h"

#include <assert.h>
#include <math.h>

float Mat44::PI = 3.1415926; 

Mat44::Mat44()
{
	memset(e, 0, sizeof(e));
	pe = &e[0][0];

	setIdentity();		// 默认设置为4阶 E 矩阵
}


Mat44::~Mat44()
{
}

// 设置所有的元素值
int Mat44::setAllElement(float fVal[16], int iCount)
{
	if (!fVal)
		return Mat44_ERR_param_is_null;

	for (int i = 0; i < iCount; i++)
		pe[i] = fVal[i];

	return 0;
}

// 设置元素值，用一维数组 16 个下标的方式
int Mat44::setElement16(int iIndex, float fVal)
{
	if (iIndex < 0 || iIndex >= ELEMENT_COUNT)
		return Mat44_ERR_index_over;

	pe[iIndex] = fVal;
	return 0;
}

// 获得元素值，用一维数组 16 个下标的方式
int Mat44::getElement16(int iIndex, float& fVal) const
{
	if (iIndex < 0 || iIndex >= ELEMENT_COUNT)
		return Mat44_ERR_index_over;

	fVal = pe[iIndex];
	return 0;
}

int Mat44::setElement44(int iRow, int iCol, float fVal)
{
	if (iRow < 0 || iRow >= ROW_COUNT)
		return Mat44_ERR_index_over;

	if (iCol < 0 || iCol >= COL_COUNT)
		return Mat44_ERR_index_over;

	e[iRow][iCol] = fVal;
	return 0;
}

int Mat44::getElement44(int iRow, int iCol, float& fVal)  const
{
	if (iRow < 0 || iRow >= ROW_COUNT)
		return Mat44_ERR_index_over;

	if (iCol < 0 || iCol >= COL_COUNT)
		return Mat44_ERR_index_over;

	fVal = e[iRow][iCol];
	return 0;
}

// 设置为4阶 E 矩阵，即如下矩阵
void Mat44::setIdentity()
{
	pe[0] = 1.0f;
	pe[1] = 0.0f;
	pe[2] = 0.0f;
	pe[3] = 0.0f;
	pe[4] = 0.0f;
	pe[5] = 1.0f;
	pe[6] = 0.0f;
	pe[7] = 0.0f;
	pe[8] = 0.0f;
	pe[9] = 0.0f;
	pe[10] = 1.0f;
	pe[11] = 0.0f;
	pe[12] = 0.0f;
	pe[13] = 0.0f;
	pe[14] = 0.0f;
	pe[15] = 1.0f;
}

void Mat44::setZero()
{
	memset(e, 0, sizeof(e));
}

// 根据移动的距离建立移动矩阵
void Mat44::createTranslation(float xTranslation, float yTranslation, float zTranslation, Mat44& dst)
{
	dst.setIdentity();

	dst.pe[12] = xTranslation;
	dst.pe[13] = yTranslation;
	dst.pe[14] = zTranslation;
}

// 根据绕Z轴旋转的角度建立绕Z轴旋转的矩阵
void Mat44::createRotationZ(float angle, Mat44& dst)
{
	dst.setIdentity();

	// 弧度值
	float radian = angle * PI / 180;

	/*
	注意：本矩阵按如下公式，是按逆时针旋转的。而 cocos2d 是按顺时针旋转的，所以符号要取反
	                                  | cosa  sina   0   0 |
		                              | -sina  cosa  0   0 |
		[ x0  y0  0  1 ]	 *	  |      0      0     1   0 |  = [ x1  y1  0  1]    
									  |	     0      0     0   1 |
	*/
	radian = -radian;

	float c = cos(radian);
	float s = sin(radian);

	dst.pe[0] = c;
	dst.pe[1] = s;
	dst.pe[4] = -s;
	dst.pe[5] = c;
}

// 获得 a 矩阵第 iRow 行与 b 矩阵第  iCol 列相乘的结果值
int Mat44::get_MatARow_MatBCol_Multi(const Mat44* a, int aRowIndex, const Mat44* b, int bColNoIndex, float& ce)
{
	if (!a)
		return Mat44_ERR_param_is_null;
	if (!b)
		return Mat44_ERR_param_is_null;

	int iRes = 0;

	float ae = 0.0;    // a 矩阵的指定元素值
	float be = 0.0;	//  b 矩阵的指定元素值

	int aRow = aRowIndex;		// a 矩阵的行
	int aCol = 0;							// a 矩阵的例
	int bRow = 0;						// b 矩阵的行
	int bCol = bColNoIndex;	// b 矩阵的例

	for (int k = 0; k < ROW_COUNT; k++)
	{
		iRes = a->getElement44(aRow, aCol, ae);		// 根据行和列获得元素值
		assert(0 == iRes);

		iRes = b->getElement44(bRow, bCol, be);		// 根据行和列获得元素值
		assert(0 == iRes);

		ce += ae*be;   // c 的元素

		aCol++;			// a 的列向右移
		bRow++;		// b 的行向下移
	}

	return 0;
}

// 矩阵相乘
int Mat44::multiplication(const Mat44* aIn, const Mat44* bIn, Mat44* c)
{
	if (!aIn)
		return Mat44_ERR_param_is_null;
	if (!bIn)
		return Mat44_ERR_param_is_null;
	if (!c)
		return Mat44_ERR_param_is_null;

	Mat44* a = const_cast<Mat44*>(aIn);		// const 强转
	Mat44* b = const_cast<Mat44*>(bIn);		// const 强转

	int iRes = 0;
	float ce = 0.0;	//  c 矩阵的指定元素值

	for (int i = 0; i < Mat44::ROW_COUNT; i++)
	{
		for (int j = 0; j < Mat44::COL_COUNT; j++)
		{
			ce = 0.0;  // 清0，必需的

			// 获得 a 矩阵第 i 行与 b 矩阵第  j 列相乘的结果值
			iRes = get_MatARow_MatBCol_Multi(a, i, b, j, ce);
			assert(0 == iRes);

			if (0 == iRes)
			{
				iRes = c->setElement44(i, j, ce);
				assert(0 == iRes);
			}

		}
	}

	return 0;
}

// 矩阵乘顶点
// 注意：此函数在 cocos2d 的矩阵变换里面用不到，因为 cocos2d 的公式为： 顶点0 * 变换矩阵 = 顶点1
int Mat44::matMultipyVertex(const Mat44* aIn, const VERTEX4* bIn, VERTEX4* c)
{
	if (!aIn)
		return Mat44_ERR_param_is_null;
	if (!bIn)
		return Mat44_ERR_param_is_null;
	if (!c)
		return Mat44_ERR_param_is_null;

	Mat44* a = const_cast<Mat44*>(aIn);				// const 强转
	VERTEX4* b = const_cast<VERTEX4*>(bIn);		// const 强转

	int iRes = 0;
	float ce[ROW_COUNT];    // c 顶点的元素
	memset(ce, 0, sizeof(ce));

	for (int iRow = 0; iRow < ROW_COUNT; iRow++)
	{
		float be[ROW_COUNT];       // b 顶点的元素
		memset(be, 0, sizeof(be));

		float ae[COL_COUNT];		// a 矩阵的元素值
		memset(ae, 0, sizeof(ae));

		for (int iCol = 0; iCol < COL_COUNT; iCol++)  // 第几列
		{
			iRes = a->getElement44(iRow, iCol, ae[iCol]);
			assert(0==iRes);
		}

		ce[iRow] += ae[0] * b->x;
		ce[iRow] += ae[1] * b->y;
		ce[iRow] += ae[2] * b->z;
		ce[iRow] += ae[3] * b->w;
	}

	c->x = ce[0];
	c->y = ce[1];
	c->z = ce[2];
	c->w = ce[3];

	return 0;
}

// 顶点乘矩阵，因为 cocos2d 的公式为： 顶点0 * 变换矩阵 = 顶点1
int Mat44::vertexMultipyMat(const VERTEX4* aIn, const Mat44* bIn, VERTEX4* c)
{
	if (!aIn)
		return Mat44_ERR_param_is_null;
	if (!bIn)
		return Mat44_ERR_param_is_null;
	if (!c)
		return Mat44_ERR_param_is_null;

	VERTEX4* a = const_cast<VERTEX4*>(aIn);				// const 强转
	Mat44* b = const_cast<Mat44*>(bIn);		// const 强转

	int iRes = 0;
	float ce[COL_COUNT];    // c 顶点的元素
	memset(ce, 0, sizeof(ce));

	for (int iCol = 0; iCol < COL_COUNT; iCol++)   // b的列
	{
		float be[ROW_COUNT];       // b 的 第 iCol 列的所有元素值
		memset(be, 0, sizeof(be));

		for (int iRow = 0; iRow < COL_COUNT; iRow++)  // b的行
		{
			iRes = b->getElement44(iRow, iCol, be[iRow]);
			assert(0 == iRes);
		}

		ce[iCol] += a->x * be[0];
		ce[iCol] += a->y * be[1];
		ce[iCol] += a->z * be[2];
		ce[iCol] += a->w * be[3];
	}

	c->x = ce[0];
	c->y = ce[1];
	c->z = ce[2];
	c->w = ce[3];

	return 0;
}

// 根据角度值获得弧度值
float Mat44::angleToRadian(float angle)
{
	// 弧度，圆周率 PI（3.1415326）
	float radian = angle * Mat44::PI  /  180;
	return radian;
}

// 根据弧度值获得角度值
float Mat44::radianToAngle(float radian)
{
	// 弧度，圆周率 PI（3.1415326）
	float angle = 180*radian/Mat44::PI;

	return angle;
}
